package org.zxp.thread.atomic;

import org.junit.Test;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * @program: zxp-thread-test
 * @description: 原子类测试
 * @author: X-Pacific zhang
 * @create: 2019-05-23 11:41
 **/
public class AtomicTest {

    public static void main(String[] args) throws InterruptedException {
//        AtomicLongArray、AtomicReferenceArray、AtomicIntegerArray
//        AtomicInteger num = new AtomicInteger(0);
//        AtomicBoolean bool = new AtomicBoolean(true);
//        AtomicLong numLong = new AtomicLong(0L);
//        AtomicReference<Inner> obj = new AtomicReference(new Inner("zxp"));
//        for (int i = 0; i < 1000; i++) {
//            new Thread(() -> num.incrementAndGet()).start();
//        }
//        for (int i = 0; i < 10001; i++) {
//            new Thread(() -> bool.getAndSet(!bool.get())).start();
//        }
//        for (int i = 0; i < 1000; i++) {
//            new Thread(() -> numLong.incrementAndGet()).start();
//        }
//        Thread.sleep(3000L);
//        //不会发生线程安全问题，无阻塞原子操作（乐观锁）
//        System.out.println(num.get());
//        System.out.println(bool.get());
//        System.out.println(numLong.get());
//        //原子的由zxp更新为zxp2
//        System.out.println(obj.getAndUpdate(o -> new Inner("zxp2")));
//        System.out.println(obj.get());
//        //原子做λ表达式中的内容
//        //传入一个新的zxp3，并打印新的zxp3和老的zxp2，并创建一个新的zxp2-zxp3
//        obj.getAndAccumulate(new Inner("zxp3"),(o, o2) -> {
//            System.out.println("pre:"+o);
//            System.out.println("next:"+o2);
//            return new Inner(o.getName()+"-"+o2.getName());
//        });
//        System.out.println(obj.get());


//        DoubleAdder、LongAdder
//        DoubleAdder db = new DoubleAdder();
//        LongAdder la = new LongAdder();
//        for (int i = 0; i < 1000; i++) {
//            new Thread(() -> db.add(1D)).start();
//        }
//
//        for (int i = 0; i < 1000; i++) {
//            new Thread(() -> la.add(1L)).start();
//        }
//        Thread.sleep(3000L);
//        //不会发生线程安全问题，无阻塞原子操作（乐观锁）
//        System.out.println(db.doubleValue());
//        System.out.println(la.longValue());



//        LongAccumulator longAccumulator;
//        DoubleAccumulator doubleAccumulator = new DoubleAccumulator((left, right) -> {
//            System.out.println("left:"+left);//identity 设定的就是left
//            System.out.println("right:"+right);//right 由accumulate方法入参设定，并调用λ表达式内代码
//            return left + right;//计算结果再次计入left
//        },1D);
//        doubleAccumulator.accumulate(2D);
//        System.out.println(doubleAccumulator.get());
//        doubleAccumulator.accumulate(3D);
//        System.out.println(doubleAccumulator.get());


//        AtomicLongArray、AtomicIntegerArray、AtomicReferenceArray数组
//        int[] arr = {1,2};
//        AtomicIntegerArray aia = new AtomicIntegerArray(arr);
//        System.out.println(aia.addAndGet(1,2));//4 将下标为1的加2并返回
//        System.out.println(aia.accumulateAndGet(1,100,(left,right) -> {
//            System.out.println("before:"+left + " next:"+right);
//            return left+right;
//        }));//104 将下标为1的(left)+新传入的值(right) 并返回

//        字段更新
//        AtomicIntegerFieldUpdater
//        AtomicLongFieldUpdater
//        AtomicReferenceFieldUpdater
        //不能父类 Integer、Long不能包装类 必须volitile 不能static

        //带有版本戳的原子操作，解决ABA问题
        String str1 = "aaa";
        String str2 = "bbb";
        AtomicStampedReference<String> reference = new AtomicStampedReference<String>(str1,1);//初始值、初始版本
        /**
         * 期望值
         * 更新值
         * 期望版本戳
         * 更新版本戳
         * （只有期望值和期望戳都满足才会更新）
         */
        System.out.println(reference.compareAndSet(str1,str2,reference.getStamp(),reference.getStamp()+1));
        /**
         * 期望值、期望版本戳都是执行时内存的值
         *相当于上面compareAndSet(str1,str2,reference.getStamp(),reference.getStamp()+1)
         */
        boolean b = reference.attemptStamp(str2, reference.getStamp() + 1);
    }


    @Test
    public void testAtomic(){
        /**
         * 除了直接的set get
         * 就是先得到再加/减
         * 要不就是先加/减再得到
         * CAS的操作，符合期望值就更新并返回true否则返回false
         * 传入一个新值，这时可以自定义逻辑（操作新值、老值），并设定return(get新旧值方法不同)：accumulateAndGet/getAndAccumulate
         * 按照自定义逻辑操作当前值，并设定return(get新旧值方法不同)：getAndUpdate/updateAndGet
         */
        AtomicInteger num = new AtomicInteger(0);
        System.out.println(num.incrementAndGet());//1  先加1再get
        System.out.println(num.decrementAndGet());//0  先减1再get
        num.set(1);//设置为1
        System.out.println("before:"+num.getAndSet(2)+" after:"+num.get()); //2 获取后set
        System.out.println(num.addAndGet(1));//3 加指定值后get
        System.out.println(num.compareAndSet(4,5));//false 如果是4 就更新成5 现在值是3无法更新
        System.out.println(num.compareAndSet(3,4));//true 如果是3 就更新成4 更新成功
        System.out.println(num.get());//4
//        num.lazySet(5);//以非volatile方式写
//        num.weakCompareAndSet()//与cas底层实现一样，先不管
        //传入一个新值，这时可以自定义逻辑（操作新值、老值），并get设定return的值
        num.accumulateAndGet(5,(left, right) -> {
            System.out.println("before:"+left+" next:"+right);
            return right;//返回5
        });
        System.out.println(num.get());//5
        //按照自定义逻辑操作当前值，先get原值并设定return
        System.out.println(num.getAndUpdate(operand -> {return operand + 1;}));//6

    }

    static class Inner{
        private String name;

        public Inner(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        @Override
        public String toString() {
            return "Inner{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }
}
